  #include "mystdio.h"

FILE_ *fopen_(const char *path_name, const char *mode)
{
    int flags = 0;
    int default_mode = 0666;
    if(strcmp(mode,"r") == 0)
    {
        flags |= O_RDONLY;
    }
    else if(strcmp(mode,"w") == 0)
    {
        flags |= (O_WRONLY | O_CREAT | O_TRUNC);
    }
    else if(strcmp(mode,"a") == 0) 
    {
        flags |= (O_WRONLY | O_CREAT | O_APPEND);
    }
    umask(0000);

    int fd = 0;
    if(flags & O_RDONLY) fd = open(path_name ,flags);
    else fd = open(path_name, flags,default_mode);
    
    if(fd < 0)
    {
        const char *error_msg = strerror(errno);
        write(2, error_msg, strlen(error_msg));
        return NULL; // 打开文件失败，返回空指针
    }
    FILE_ *fp = (FILE_ *)malloc(sizeof(FILE_));
    assert(fp);// malloc申请空间必须成功

    fp->flags = SYNC_LINE;// 默认设置为行刷新
    fp->fileno = fd;
    fp->capacity = SIZE;
    fp->size = 0;
    memset(fp->buffer, 0, SIZE);//将所有字节初始化为\0
    
    return fp;// 返回FILE_*指针                                                                                                                                          
     
}
void fwrite_(const void *ptr, int num, FILE_ *fp)
{
    //1.将数据写入到语言级缓冲区里
    memcpy(fp->buffer + fp->size, ptr, num);
    //加fp->size的原因是因为打开文件的方式有可能是追加。
    //这里不考虑缓冲区溢出的问题，如果你想考虑可以通过realloc的方式来解决，
    
    fp->size += num;//更新FILE_中的buffer当前使用量
    
    //2.判断是否满足刷新策略，如果满足那就刷新，不满足就不刷新
    if(fp->flags & SYNC_NOW)
    {                                                                                                                                                                    
        write(fp->fileno, fp->buffer, num);
        fp->size = 0;//相当于清空缓冲区，下次写入时直接覆盖原有缓冲区内容
        //惰性释放
    }
    else if(fp->flags & SYNC_LINE)
    {
        //暂时不考虑abc\ndef这种情况，处理这种情况可以利用for循环遍历,记录\n位置并将\n之前的数据刷新到磁盘外设文件中。
        if(fp->buffer[fp->size-1] == '\n')
        {
            write(fp->fileno,fp->buffer,fp->size);
            fp->size = 0;//清空缓冲区
        }
    }
    else if(fp->flags & SYNC_FULL)
    {
        if(fp->size == fp->capacity) 
        {
            write(fp->fileno, fp->buffer, num);
            fp->size = 0;//清空缓冲区
        }
    }
}
void fflush_(FILE_ *fp)
{
    //fflush做两件事情，1.用户缓冲区数据->内核 2.内核数据->外设
   
    //系统调用write可以直接将数据写到内核缓冲区里。
    if( fp->size > 0 ) write(fp->fileno, fp->buffer,fp->size);
    //实际上write可以将任何数据直接写到内核缓冲区中。
    fsync(fp->fileno); // 将内核缓冲区数据强制性刷新到外设里

    fp->size = 0;//清空缓冲区
}
void fclose_(FILE_ *fp)
{
     //fclose关闭文件，需要先进行语言级缓冲区刷新，然后再关闭文件描述符
     fflush_(fp);
     close(fp->fileno);
}

